﻿(function () {
	'use strict';
	var ObritControl = function (params) {
		var defaults = {
			radius: 50,
			deceleration: 0.1,
			container: document.body,
			onStart() {
			},
			onMove() {
			},
			onEnd() {
			},
			onChange() {
			}
		}
		params = params || {};
		for (var def in defaults) {
			if (typeof params[def] === 'undefined') {
				params[def] = defaults[def];
			}
			else if (typeof params[def] === 'object') {
				for (var deepDef in defaults[def]) {
					if (typeof params[def][deepDef] === 'undefined') {
						params[def][deepDef] = defaults[def][deepDef];
					}
				}
			}
		}
		this.config = params;
		this.lat = this.lon = 0;
		this.lastX = this.lastY = 0;
		this.deltaX = this.deltaY = 0;
		this.lastDistance = 0;
		this.startX = this.startY = 0;
		this.speed = { lat: 0, lon: 0 };
		this.factor = 50 / this.config.radius;
		this.bind();
	}
	ObritControl.prototype.bind = function () {
		this.config.container.addEventListener('mousedown', this._bindMouseDown = this._onMouseDown.bind(this), { passive: false });
		this.config.container.addEventListener('touchstart', this._bindStart = this._onStart.bind(this), { passive: false });
		this.config.container.addEventListener('touchmove', this._bindMove = this._onMove.bind(this), { passive: false });
		this.config.container.addEventListener('touchend', this._bindEnd = this._onEnd.bind(this), { passive: false });

	};
	ObritControl.prototype.unbind = function () {
		this.config.container.removeEventListener('touchstart', this._bindStart);
		this.config.container.removeEventListener('touchmove', this._bindMove);
		this.config.container.removeEventListener('touchend', this._bindEnd);
	};
	ObritControl.prototype._onMouseDown = function (event) {
		event.preventDefault();
		this.config.container.addEventListener('mousemove', this._bindMouseMove = this._onMouseMove.bind(this), { passive: false });
		this.config.container.addEventListener('mouseup', this._bindMouseUp = this._onMouseUp.bind(this), { passive: false });
		this.config.container.addEventListener('mouseout', this._bindMouseUp = this._onMouseUp.bind(this), { passive: false });
		this.config.onStart();
	};
	ObritControl.prototype._onMouseMove = function (event) {
		var movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0;
		var movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0;
		this.deltaX = -movementX * 0.3;
		this.deltaY = movementY * 0.3;
		this.lon += this.deltaX;
		this.lat += this.deltaY;
		this.config.onChange({
			X: this.lon,
			Y: this.lat,
			deltaY: this.deltaY,
			deltaX: this.deltaX
		});
	};
	ObritControl.prototype._onMouseUp = function (event) {
		this.config.container.removeEventListener('mousemove', this._bindMouseMove);
		this.config.container.removeEventListener('mouseup', this._bindMouseUp);
		this.config.container.removeEventListener('mouseout', this._bindMouseUp);
		this.config.onEnd();
	};
	ObritControl.prototype._onStart = function (event) {
		const evt = event.changedTouches[0];
		this.startX = this.lastX = evt.clientX;
		this.startY = this.lastY = evt.clientY;
		this.startTime = Date.now();
		this.config.onStart(event);
		this.speed = { lat: 0, lon: 0 };
		this.lastDistance = undefined;
		this.config.onStart();
	};
	ObritControl.prototype._onMove = function (event) {
		event.preventDefault();
		const evt = event.changedTouches[0];
		switch (event.changedTouches.length) {
			case 1:
				if (!this.lastDistance) {
					this.deltaX = (this.lastX - evt.clientX) * this.factor;
					this.deltaY = (evt.clientY - this.lastY) * this.factor;

					this.lon += this.deltaX;
					this.lat += this.deltaY;

					this.lastX = evt.clientX;
					this.lastY = evt.clientY;

					this.config.onChange({
						X: this.lon,
						Y: this.lat,
						deltaY: this.deltaY,
						deltaX: this.deltaX
					});
				}
				break;
			case 2:
				const evt1 = event.changedTouches[1];
				let distance = Math.abs(evt.clientX - evt1.clientX) + Math.abs(evt.clientY - evt1.clientY);
				if (this.lastDistance === undefined) {
					this.lastDistance = distance;
				}
				let scale = distance / this.lastDistance;

				if (scale) {
					this.config.onChange({ scale });
					this.lastDistance = distance;
				}
		}
		this.config.onMove(event);
	};
	ObritControl.prototype._onEnd = function (event) {
		//惯性
		let t = (Date.now() - this.startTime) / 3;
		this.speed = {
			lat: (this.startY - this.lastY) / t,
			lon: (this.startX - this.lastX) / t
		};

		this._inertance();
		this.config.onEnd(event);

	};
	ObritControl.prototype._subSpeed = function (speed) {
		if (speed !== 0) {
			if (speed > 0) {
				speed -= this.config.deceleration;
				speed < 0 && (speed = 0);
			} else {
				speed += this.config.deceleration;
				speed > 0 && (speed = 0);
			}
		}
		return speed;
	};
	ObritControl.prototype._inertance = function () {
		const speed = this.speed;
		speed.lat = this._subSpeed(speed.lat);
		speed.lon = this._subSpeed(speed.lon);
		this.deltaY = -speed.lat;
		this.deltaX = speed.lon;
		this.lat += this.deltaY;
		this.lon += this.deltaX;


		this.config.onChange({
			isUserInteracting: false,
			speed,
			X: this.lon,
			Y: this.lat,
			deltaY: this.deltaY,
			deltaX: this.deltaX
		});
		if (speed.lat === 0 && speed.lon === 0) {
			this._intFrame && cancelAnimationFrame(this._intFrame);
			this._intFrame = 0;
		} else {
			this._intFrame = requestAnimationFrame(this._inertance.bind(this));
		}
	};
	window.ObritControl = ObritControl;

})(window);